import { isBoolean, isPlainObject, forEach, trim, isString, every, includes, groupBy } from 'lodash';
import bigDecimal from 'js-big-decimal';

import { numberFormat } from '@/utils/formatterUtil';

import { cpfFormulaType } from '@/pages/SalaryManage/enum';

const util = {
    /**
     * 遍历数据行，并返回与回调（callback）结果合并的结果
     * @param {Array | Object} rows 数据
     * @param callback 回调
     * @returns {(*)|*}
     */
    mapRows(rows, callback) {
        if (!rows) {
            return rows;
        }

        if (Array.isArray(rows)) {
            return rows.map(row => ({ ...row, ...callback(row) }));
        }

        return { ...rows, ...callback(rows) };
    },

    delayExecute: fn => () => {
        setTimeout(fn, 50);
    },

    /**
     * 是否需要显示"上限"
     * @param row
     * @returns {*|boolean|boolean|boolean}
     */
    needShowMax(row) {
        if (isBoolean(row.vvShowMax)) {
            return row.vvShowMax;
        }

        return !!trim(row.maxAmt);
    },

    /**
     * 是否需要校验
     * @param validateFields
     * @param { * | Array}allValidateFields
     * @returns {boolean}
     */
    needValidate(validateFields, allValidateFields) {
        if (!allValidateFields || allValidateFields === '*') {
            return true;
        }

        if (isString(validateFields) && includes(allValidateFields, validateFields)) {
            return true;
        }

        return every(validateFields, field => includes(allValidateFields, field));
    },

    /**
     * 校验单行的公式是否正确
     * @param record 要校验的数据
     * @param validateFields 要校验的字段
     * @returns {{errorField, message}}
     */
    validateFormulaRow(record, validateFields) {
        const errorField = {};
        const errorMessages = [];

        const validateResult = (fieldMap, messages) => ({
            errorField: fieldMap,
            message: messages.join(', ')
        });

        if (!record.formulaType) {
            errorField.formulaType = true;
            errorMessages.push('请选择公式');
            return validateResult(errorField, errorMessages);
        }

        if (
            util.needValidate('shareRate', validateFields) &&
            record.formulaType !== cpfFormulaType.NIL &&
            !trim(record.shareRate)
        ) {
            errorField.shareRate = true;
            errorMessages.push('请输入承担比例');
        }

        if (
            util.needValidate('shareAmt', validateFields) &&
            record.formulaType === cpfFormulaType.TWB &&
            !trim(record.shareAmt)
        ) {
            errorField.shareAmt = true;
            errorMessages.push('请输入金额');
        }

        if (util.needValidate('maxAmt', validateFields) && record.vvShowMax && !trim(record.maxAmt)) {
            errorField.maxAmt = true;
            errorMessages.push('请输入上限');
        }

        return errorMessages.length === 0 ? validateResult({}, []) : validateResult(errorField, errorMessages);
    },

    /**
     * 获取公式描述
     * @param formulaDataList
     * @param decimalPlaces
     * @param thousandsSeparator
     * @returns {string}
     */
    getFormulaDescription(formulaDataList, decimalPlaces = 2, thousandsSeparator = ',') {
        const errorFormulaMessage = '预览';

        if (!formulaDataList || formulaDataList.length === 0) {
            return '';
        }
        const list = isPlainObject(formulaDataList) ? [formulaDataList] : formulaDataList;

        // 只要找到有一个校验不通过的行，直接返回
        if (list.find(rowData => util.validateFormulaRow(rowData).message)) {
            return errorFormulaMessage;
        }

        // 按分组
        const formulaTypeMap = groupBy(list, 'formulaType');
        const formulaTypeKeys = Object.keys(formulaTypeMap);

        // 如果全是Nil时，直接返回Nil
        if (formulaTypeKeys.length === 1 && +formulaTypeKeys[0] === cpfFormulaType.NIL) {
            return 'Nil';
        }

        const descriptionArray = [];
        const maxAmtDescriptionArray = [];
        const addMaxAmtSymbol = (content, needShowAmt) => (needShowAmt ? `[${content}]*` : content);
        forEach(Object.entries(formulaTypeMap), ([formulaTypeStr, rowArray]) => {
            const formulaType = +formulaTypeStr;
            let formulaDescription;
            let tempDescArray;

            const total = rowArray.reduce((sum, row) => bigDecimal.add(sum, row.shareRate), 0);
            const shareRateFormat =
                total != null ? `${numberFormat(bigDecimal.multiply(total, 100), decimalPlaces)}%` : null;

            const needMaxAmtDesc =
                includes([cpfFormulaType.TW, cpfFormulaType.AW, cpfFormulaType.OW], formulaType) &&
                every(rowArray, row => util.needShowMax(row));

            if (needMaxAmtDesc) {
                const totalMaxAmt = rowArray.reduce((sum, row) => bigDecimal.add(sum, row.maxAmt), 0);
                maxAmtDescriptionArray.push(
                    ` *Max.of $${numberFormat(totalMaxAmt, decimalPlaces, thousandsSeparator)} `
                );
            }

            switch (formulaType) {
                case cpfFormulaType.TW:
                    formulaDescription = addMaxAmtSymbol(`${shareRateFormat}(TW)`, needMaxAmtDesc);
                    break;

                case cpfFormulaType.AW:
                    formulaDescription = addMaxAmtSymbol(`${shareRateFormat}(AW)`, needMaxAmtDesc);
                    break;

                case cpfFormulaType.OW:
                    formulaDescription = addMaxAmtSymbol(`${shareRateFormat}(OW)`, needMaxAmtDesc);
                    break;

                case cpfFormulaType.TWB:
                    tempDescArray = rowArray.map(row => {
                        const { shareRate, shareAmt } = row;
                        const rateFormat = trim(shareRate)
                            ? `${numberFormat(bigDecimal.multiply(row.shareRate, 100), decimalPlaces)}%`
                            : null;
                        const shareAmtFormat = trim(shareAmt)
                            ? `${numberFormat(shareAmt, decimalPlaces, thousandsSeparator)}`
                            : null;

                        if (util.needShowMax(row)) {
                            maxAmtDescriptionArray.push(
                                ` *Max.of $${numberFormat(row.maxAmt, decimalPlaces, thousandsSeparator)} `
                            );
                        }
                        return addMaxAmtSymbol(`${rateFormat}(TW - $${shareAmtFormat})`, util.needShowMax(row));
                    });
                    formulaDescription = tempDescArray.join(' + ');
                    break;

                default:
                    break;
            }

            if (formulaDescription) {
                descriptionArray.push(formulaDescription);
            }
        });

        let maxAmtMessage = '';
        if (maxAmtDescriptionArray?.length > 0) {
            maxAmtMessage += `\r\n${maxAmtDescriptionArray.join(' ')}`;
        }

        return descriptionArray.join(' + ') + maxAmtMessage;
    }
};

export default util;
